package com.mazaiting.ftp.utils;

import com.mazaiting.ftp.constant.FtpConstant;
import com.mazaiting.ftp.domain.entity.FtpConfig;
import com.mazaiting.ftp.exception.FtpException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * FTP 管理工具
 *
 * @author mazaiting
 * @create_time 2022/4/21 16:05
 */
public class FtpUtil {
    /**
     * 日志工具
     */
    private final Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * ftp 客户端工具
     */
    private final FTPClient ftpClient;
    /**
     * Ftp 配置
     */
    private final FtpConfig ftpConfig;

    public FtpUtil(FtpConfig config) {
        this.ftpClient = new FTPClient();
        this.ftpConfig = config;
    }

    public boolean isConnected() {
        return ftpClient.isConnected();
    }

    /**
     * 测试是否能连接
     *
     * @return 是否链接成功
     */
    public boolean connect() {
        try {
            // 设置默认编码为UTF-8
            ftpClient.setControlEncoding(StandardCharsets.UTF_8.name());
            // ftp连接
            ftpClient.connect(ftpConfig.getHost(), ftpConfig.getPort());
            // 判断是否连接成功,获取返回replyCode，注意：此处返回最后一次执行的ftp操作码值
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                throw new FtpException(FtpConstant.FTP_CONNECT_ERROR);
            }
            // 设置为被动模式
            ftpClient.enterLocalPassiveMode();
            // FTP进行登录
            if(! ftpClient.login(ftpConfig.getUsername(), ftpConfig.getPassword()) ){
                throw new FtpException(FtpConstant.FTP_LOGIN_ERROR);
            }
            // 判断二进制传输是否设置成功，设置传输模式(默认为ASCII)，设置为二进制setFileType(FTP.BINARY_FILE_TYPE);
            if(! ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE) ){
                throw new FtpException(FtpConstant.BINARY_FILE_TYPE_ERROR);
            }
        } catch (IOException e) {
            logger.error("FTP连接失败,失败的可能原因：\r\n1.地址和端口号错误\r\n2.FileType设置失败\r\n错误的用户名或密码");
            // 关闭Ftp服务
            disconnect();
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 上传文件
     *
     * @param saveName        全路径。如/home/public/a.txt
     * @param fileInputStream 要上传的文件流
     */
    public boolean storeFile(String saveName, InputStream fileInputStream) {
        boolean flag = false;
        try {
            // 文件名设置转码
            String remoteFileName = java.net.URLDecoder.decode(saveName, StandardCharsets.UTF_8);
            // 判断文件是否上传成功 ,inputStream
            if(!ftpClient.storeFile(remoteFileName, fileInputStream)){
                logger.error("文件上传失败");
            }
        } catch (IOException e) {
            logger.error("文件上传失败");
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * FTP 文件夹创建
     *
     * @param basePath  需要创建的目录，案例：/images/img
     * @return 返回文件夹是否创建成功
     */
    public boolean mkdirDirectory(String basePath) {
        try {
            // 判断当前目录是否存在
            if (!ftpClient.changeWorkingDirectory(basePath)) {
                //如果目录不存在创建目录
                String[] dirs = basePath.split("/");
                // 遍历需要创建的路径
                for (String dir : dirs) {
                    // 判断，拼接的首部或者尾部可能为空，为空直接跳过
                    if (Objects.isNull(dir) || dir.isEmpty()) {
                        continue;
                    }
                    // 按层级创建目录
                    boolean pathFlag = ftpClient.changeWorkingDirectory(dir);
                    if (!pathFlag) {
                        int replyCode = ftpClient.getReplyCode();
                        // 判断文件是否创建失败
                        boolean mkdirFlag = ftpClient.makeDirectory(dir);
                        if (!mkdirFlag) {
                            logger.error("文件不存在或者创建失败");
                            return false;
                        } else {
                            // 新路径
                            ftpClient.changeWorkingDirectory(dir);
                        }
                    }
                }
            }
        } catch (IOException e) {
            logger.error("文件不存在或者创建失败");
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 断开FTP连接
     */
    public void disconnect() {
        // 判断是否在连接
        if (ftpClient.isConnected()) {
            try {
                // 关闭连接
                ftpClient.disconnect();
                logger.info("ftpClient关闭连接成功");
            } catch (IOException e) {
                logger.info("ftpClient关闭连接失败");
                e.printStackTrace();
            }
        }
    }

    /**
     * 赋值新名字
     *
     * @param file 文件名
     * @param name 新名字
     * @return 返回名字
     */
    private String getFileName(File file, String name) {
        // 获取主动设置的文件名
        if ( !( Objects.isNull(name) || name.isEmpty() ) ) {
            String fileName = file.getName();
            return name + fileName.substring(fileName.lastIndexOf("."));
        } else {
            return file.getName();
        }
    }

    /**
     * 根据路径创建文件夹
     *
     * @param localPath 路径
     * @return 返回一个file对象
     */
    public File createLocalDir(String localPath) {
        File file = new File(localPath);
        // 判断file是否是路径
        if (file.isAbsolute()) {
            // 不存在则
            if (!file.exists()) {
                // 创建多级文件夹
                if (!file.mkdirs()) {
                    logger.error("文件夹创建失败");
                }
            }
        }
        return file;
    }

    /**
     * 处理路径的 / 方法
     * @param rootDir 服务器根路径
     * @param remoteDir 指定的相对路径
     * @return 返回全路径
     */
    public String getRemoteDir(String rootDir, String remoteDir){
        // 设置返回路径
        String currentDir = "";
        // 处理根路径开头 无 / 的情况
        if(!rootDir.startsWith("/")){
            rootDir ="/" + rootDir;
        }
        // 处理根路径接货 的 /  和相对路径 开头 / 冲突 的情况
        if (rootDir.endsWith("/")) {
            if (remoteDir.startsWith("/")) {
                currentDir = rootDir + remoteDir.substring(1);
            } else {
                currentDir = rootDir + remoteDir;
            }
        } else {
            if (remoteDir.startsWith("/")) {
                currentDir = rootDir + remoteDir;
            } else {
                currentDir = rootDir  + "/" + remoteDir;
            }
        }
        return currentDir;
    }

    /**
     * 文件下载
     * @param remoteName 文件名称
     * @param os  输出流
     * @return 是否下载成功
     */
    public boolean retrieveFile(String remoteName, OutputStream os){
        boolean flag = false;
        try {
            flag = ftpClient.retrieveFile(remoteName,os);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }

    public boolean changeWorkingDirectory(String remoteDir){
        boolean flag = false;
        try {
            flag = ftpClient.changeWorkingDirectory(remoteDir);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;

    }

    public FTPFile[] listFiles(){
        FTPFile[] files = null;
        try {
            files = ftpClient.listFiles();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return files;
    }

}
